/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package com.python.pydev.analysis.actions;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.SortedMap;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ResourceWorkingSetFilter;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;
import org.eclipse.ui.actions.WorkingSetFilterActionGroup;
import org.eclipse.ui.dialogs.FilteredItemsSelectionDialog;
import org.eclipse.ui.statushandlers.StatusManager;
import org.python.pydev.core.ExtensionHelper;
import org.python.pydev.core.ICodeCompletionASTManager;
import org.python.pydev.core.IInterpreterInfo;
import org.python.pydev.core.IModulesManager;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.ModulesKey;
import org.python.pydev.core.callbacks.CallbackWithListeners;
import org.python.pydev.core.callbacks.ICallbackWithListeners;
import org.python.pydev.core.log.Log;
import org.python.pydev.editor.codecompletion.revisited.CompletionCache;
import org.python.pydev.plugin.nature.PythonNature;
import org.python.pydev.ui.IViewCreatedObserver;
import org.python.pydev.ui.IViewWithControls;
import com.aptana.shared_core.structure.Tuple;
import com.python.pydev.analysis.AnalysisPlugin;
import com.python.pydev.analysis.additionalinfo.AbstractAdditionalTokensInfo;
import com.python.pydev.analysis.additionalinfo.AdditionalProjectInterpreterInfo;
import com.python.pydev.analysis.additionalinfo.AdditionalSystemInterpreterInfo;
import com.python.pydev.analysis.additionalinfo.IInfo;
import com.python.pydev.analysis.additionalinfo.InfoFactory;
import com.python.pydev.analysis.additionalinfo.ModInfo;
/**
* Let us choose from a list of IInfo (and the related additional info)
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class GlobalsTwoPanelElementSelector2 extends FilteredItemsSelectionDialog implements IViewWithControls {
private static final String DIALOG_SETTINGS = "com.python.pydev.analysis.actions.GlobalsTwoPanelElementSelector2"; //$NON-NLS-1$
private static final String WORKINGS_SET_SETTINGS = "WorkingSet"; //$NON-NLS-1$
private WorkingSetFilterActionGroup workingSetFilterActionGroup;
private CustomWorkingSetFilter workingSetFilter = new CustomWorkingSetFilter();
private String title;
private List<AbstractAdditionalTokensInfo> additionalInfo;
private String selectedText;
public final ICallbackWithListeners onControlCreated = new CallbackWithListeners();
public final ICallbackWithListeners onControlDisposed = new CallbackWithListeners();
private List createdCallbacksForControls;
public GlobalsTwoPanelElementSelector2(Shell shell, boolean multi, String selectedText) {
super(shell, multi);
this.selectedText = selectedText;
setSelectionHistory(new InfoSelectionHistory());
setTitle("PyDev: Globals Browser");
setMessage("Matching: ? = any char * = any str CamelCase (TC=TestCase) Space in the end = exact match.\n"
+ "Dotted names may be used to filter with package (e.g.: django.utils.In or just dj.ut.in)");
NameIInfoLabelProvider resourceItemLabelProvider = new NameIInfoStyledLabelProvider(true);
ModuleIInfoLabelProvider resourceItemDetailsLabelProvider = new ModuleIInfoLabelProvider();
setListLabelProvider(resourceItemLabelProvider);
setDetailsLabelProvider(resourceItemDetailsLabelProvider);
}
public void setTitle(String title) {
super.setTitle(title);
this.title = title;
}
/**
* Used to add the working set to the title.
*/
private void setSubtitle(String text) {
if (text == null || text.length() == 0) {
getShell().setText(title);
} else {
getShell().setText(title + " - " + text); //$NON-NLS-1$
}
}
protected IDialogSettings getDialogSettings() {
IDialogSettings settings = AnalysisPlugin.getDefault().getDialogSettings().getSection(DIALOG_SETTINGS);
if (settings == null) {
settings = AnalysisPlugin.getDefault().getDialogSettings().addNewSection(DIALOG_SETTINGS);
}
return settings;
}
protected void storeDialog(IDialogSettings settings) {
super.storeDialog(settings);
XMLMemento memento = XMLMemento.createWriteRoot("workingSet"); //$NON-NLS-1$
workingSetFilterActionGroup.saveState(memento);
workingSetFilterActionGroup.dispose();
StringWriter writer = new StringWriter();
try {
memento.save(writer);
settings.put(WORKINGS_SET_SETTINGS, writer.getBuffer().toString());
} catch (IOException e) {
StatusManager.getManager().handle(
new Status(IStatus.ERROR, AnalysisPlugin.getPluginID(), IStatus.ERROR, "", e)); //$NON-NLS-1$
// don't do anything. Simply don't store the settings
}
}
protected void restoreDialog(IDialogSettings settings) {
super.restoreDialog(settings);
String setting = settings.get(WORKINGS_SET_SETTINGS);
if (setting != null) {
try {
IMemento memento = XMLMemento.createReadRoot(new StringReader(setting));
workingSetFilterActionGroup.restoreState(memento);
} catch (WorkbenchException e) {
StatusManager.getManager().handle(
new Status(IStatus.ERROR, AnalysisPlugin.getPluginID(), IStatus.ERROR, "", e)); //$NON-NLS-1$
// don't do anything. Simply don't restore the settings
}
}
addListFilter(workingSetFilter);
applyFilter();
}
/**
* We need to add the action for the working set.
*/
protected void fillViewMenu(IMenuManager menuManager) {
super.fillViewMenu(menuManager);
workingSetFilterActionGroup = new WorkingSetFilterActionGroup(getShell(), new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
String property = event.getProperty();
if (WorkingSetFilterActionGroup.CHANGE_WORKING_SET.equals(property)) {
IWorkingSet workingSet = (IWorkingSet) event.getNewValue();
if (workingSet != null && !(workingSet.isAggregateWorkingSet() && workingSet.isEmpty())) {
workingSetFilter.setWorkingSet(workingSet);
setSubtitle(workingSet.getLabel());
} else {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window != null) {
IWorkbenchPage page = window.getActivePage();
workingSet = page.getAggregateWorkingSet();
if (workingSet.isAggregateWorkingSet() && workingSet.isEmpty()) {
workingSet = null;
}
}
workingSetFilter.setWorkingSet(workingSet);
setSubtitle(null);
}
scheduleRefresh();
}
}
});
menuManager.add(new Separator());
workingSetFilterActionGroup.fillContextMenu(menuManager);
}
protected Control createExtendedContentArea(Composite parent) {
return null;
}
@Override
protected Control createDialogArea(Composite parent) {
Control ret = super.createDialogArea(parent);
List<IViewCreatedObserver> participants = ExtensionHelper
.getParticipants(ExtensionHelper.PYDEV_VIEW_CREATED_OBSERVER);
for (IViewCreatedObserver iViewCreatedObserver : participants) {
iViewCreatedObserver.notifyViewCreated(this);
}
createdCallbacksForControls = callRecursively(onControlCreated, parent, new ArrayList());
return ret;
}
/**
* Calls the callback with the composite c and all of its children (recursively).
*/
private List callRecursively(ICallbackWithListeners callback, Composite c, ArrayList controls) {
try {
for (Control child : c.getChildren()) {
if (child instanceof Composite) {
callRecursively(callback, (Composite) child, controls);
}
if (child instanceof Text || child instanceof Table) {
controls.add(child);
callback.call(child);
}
}
} catch (Throwable e) {
Log.log(e);
}
return controls;
}
public Object[] getResult() {
Object[] result = super.getResult();
if (result == null)
return null;
List<AdditionalInfoAndIInfo> resultToReturn = new ArrayList<AdditionalInfoAndIInfo>();
for (int i = 0; i < result.length; i++) {
if (result[i] instanceof AdditionalInfoAndIInfo) {
resultToReturn.add((AdditionalInfoAndIInfo) result[i]);
}
}
return resultToReturn.toArray(new AdditionalInfoAndIInfo[resultToReturn.size()]);
}
/**
* Overridden to set the initial pattern (if null we have an exception, so, it must at least be empty)
*/
public int open() {
if (getInitialPattern() == null) {
setInitialPattern(selectedText == null ? "" : selectedText);
} else {
setInitialPattern("");
}
int ret = super.open();
if (this.createdCallbacksForControls != null) {
for (Object o : this.createdCallbacksForControls) {
onControlDisposed.call(o);
}
this.createdCallbacksForControls = null;
}
return ret;
}
public String getElementName(Object item) {
AdditionalInfoAndIInfo info = (AdditionalInfoAndIInfo) item;
return info.info.getName();
}
protected IStatus validateItem(Object item) {
return Status.OK_STATUS;
}
protected ItemsFilter createFilter() {
return new InfoFilter();
}
/**
* Sets the elements we should work on (must be set before open())
*/
public void setElements(List<AbstractAdditionalTokensInfo> additionalInfo) {
this.additionalInfo = additionalInfo;
}
protected Comparator<AdditionalInfoAndIInfo> getItemsComparator() {
return new Comparator<AdditionalInfoAndIInfo>() {
/*
* (non-Javadoc)
*
* @see java.util.Comparator#compare(java.lang.Object,
* java.lang.Object)
*/
public int compare(AdditionalInfoAndIInfo resource1, AdditionalInfoAndIInfo resource2) {
Collator collator = Collator.getInstance();
String s1 = resource1.info.getName();
String s2 = resource2.info.getName();
int comparability = collator.compare(s1, s2);
//same name
if (comparability == 0) {
String p1 = resource1.info.getDeclaringModuleName();
String p2 = resource2.info.getDeclaringModuleName();
if (p1 == null && p2 == null) {
return 0;
}
if (p1 != null && p2 == null) {
return -1;
}
if (p1 == null && p2 != null) {
return 1;
}
return p1.compareTo(p2);
}
return comparability;
}
};
}
/**
* This is the place where we put all the info in the content provider. Note that here we must add
* ALL the info -- later, we'll filter it based on the active working set.
*/
protected void fillContentProvider(AbstractContentProvider contentProvider, ItemsFilter itemsFilter,
IProgressMonitor progressMonitor) throws CoreException {
if (itemsFilter instanceof InfoFilter) {
if (progressMonitor != null) {
progressMonitor.beginTask("Searching...", this.additionalInfo.size());
}
for (AbstractAdditionalTokensInfo additionalInfo : this.additionalInfo) {
if (progressMonitor != null) {
if (progressMonitor.isCanceled()) {
return;
} else {
progressMonitor.worked(1);
}
}
Collection<IInfo> allTokens = new HashSet<IInfo>(additionalInfo.getAllTokens()); //no duplicates
for (IInfo iInfo : allTokens) {
contentProvider.add(new AdditionalInfoAndIInfo(additionalInfo, iInfo), itemsFilter);
}
//Also show to the user the modules available as globals (2.2.3)
IModulesManager modulesManager = null;
try {
if (additionalInfo instanceof AdditionalProjectInterpreterInfo) {
AdditionalProjectInterpreterInfo projectInterpreterInfo = (AdditionalProjectInterpreterInfo) additionalInfo;
IProject project = projectInterpreterInfo.getProject();
PythonNature nature = PythonNature.getPythonNature(project);
if (nature != null) {
ICodeCompletionASTManager astManager = nature.getAstManager();
if (astManager != null) {
modulesManager = astManager.getModulesManager();
}
}
} else if (additionalInfo instanceof AdditionalSystemInterpreterInfo) {
AdditionalSystemInterpreterInfo systemInterpreterInfo = (AdditionalSystemInterpreterInfo) additionalInfo;
IInterpreterInfo defaultInterpreterInfo = systemInterpreterInfo.getManager()
.getDefaultInterpreterInfo(false);
modulesManager = defaultInterpreterInfo.getModulesManager();
}
} catch (Throwable e) {
Log.log(e);
}
if (modulesManager != null) {
SortedMap<ModulesKey, ModulesKey> allDirectModulesStartingWith = modulesManager
.getAllDirectModulesStartingWith("");
Collection<ModulesKey> values = allDirectModulesStartingWith.values();
for (ModulesKey modulesKey : values) {
contentProvider.add(new AdditionalInfoAndIInfo(additionalInfo, new ModInfo(modulesKey.name)),
itemsFilter);
}
}
}
}
if (progressMonitor != null) {
progressMonitor.done();
}
}
/**
* Viewer filter which filters resources due to current working set
*/
private class CustomWorkingSetFilter extends ViewerFilter {
private ResourceWorkingSetFilter resourceWorkingSetFilter = new ResourceWorkingSetFilter();
public void setWorkingSet(IWorkingSet workingSet) {
resourceWorkingSetFilter.setWorkingSet(workingSet);
}
public boolean select(Viewer viewer, Object parentElement, Object element) {
if (element instanceof AdditionalInfoAndIInfo) {
AdditionalInfoAndIInfo info = (AdditionalInfoAndIInfo) element;
if (info.additionalInfo instanceof AdditionalProjectInterpreterInfo) {
AdditionalProjectInterpreterInfo projectInterpreterInfo = (AdditionalProjectInterpreterInfo) info.additionalInfo;
return resourceWorkingSetFilter.select(viewer, parentElement, projectInterpreterInfo.getProject());
}
}
return resourceWorkingSetFilter.select(viewer, parentElement, element);
}
}
/**
* Filters the info based on the pattern (considers each dot as a new scope in the pattern.)
*/
protected class InfoFilter extends ItemsFilter {
private String initialPattern;
public InfoFilter() {
super();
//We have to get the actual text from the control, because the
Text pattern = (Text) getPatternControl();
String stringPattern = ""; //$NON-NLS-1$
if (pattern != null && !pattern.getText().equals("*")) { //$NON-NLS-1$
stringPattern = pattern.getText();
}
this.initialPattern = stringPattern;
}
/**
* Must have a valid name.
*/
public boolean isConsistentItem(Object item) {
if (!(item instanceof AdditionalInfoAndIInfo)) {
return false;
}
AdditionalInfoAndIInfo iInfo = (AdditionalInfoAndIInfo) item;
if (iInfo.info.getName() != null) {
return true;
}
return false;
}
/**
* We must override it so that the results are properly updating according to the scopes in the pattern
* (if we only returned false it'd also work, but it'd need to traverse all the items at each step).
*/
public boolean isSubFilter(ItemsFilter filter) {
if (!(filter instanceof InfoFilter)) {
return false;
}
return MatchHelper.isSubFilter(this.initialPattern, ((InfoFilter) filter).initialPattern);
}
/**
* Override so that we consider scopes.
*/
public boolean equalsFilter(ItemsFilter filter) {
if (!(filter instanceof InfoFilter)) {
return false;
}
return MatchHelper.equalsFilter(this.initialPattern, ((InfoFilter) filter).initialPattern);
}
/**
* Overridden to consider each dot as a new scope in the pattern (and match according to modules)
*/
public boolean matchItem(Object item) {
if (!(item instanceof AdditionalInfoAndIInfo)) {
return false;
}
AdditionalInfoAndIInfo info = (AdditionalInfoAndIInfo) item;
return MatchHelper.matchItem(patternMatcher, info.info);
}
}
/**
* Used to store/restore the selections.
*/
private class InfoSelectionHistory extends SelectionHistory {
public InfoSelectionHistory() {
}
protected Object restoreItemFromMemento(IMemento element) {
InfoFactory infoFactory = new InfoFactory();
AdditionalInfoAndIInfo resource = (AdditionalInfoAndIInfo) infoFactory.createElement(element);
if (resource != null) {
if (resource.additionalInfo instanceof AdditionalSystemInterpreterInfo) {
AdditionalInfoAndIInfo found = checkAdditionalInfo(resource, resource.info.getName(),
(AdditionalSystemInterpreterInfo) resource.additionalInfo);
if (found != null) {
return found;
}
} else if (resource.additionalInfo instanceof AdditionalProjectInterpreterInfo) {
AdditionalProjectInterpreterInfo projectInterpreterInfo = (AdditionalProjectInterpreterInfo) resource.additionalInfo;
IProject project = projectInterpreterInfo.getProject();
if (project != null) {
List<IPythonNature> natures = new ArrayList<IPythonNature>();
PythonNature n = PythonNature.getPythonNature(project);
if (n != null) {
natures.add(n);
try {
List<Tuple<AbstractAdditionalTokensInfo, IPythonNature>> additionalInfoAndNature = AdditionalProjectInterpreterInfo
.getAdditionalInfoAndNature(n, true, false);
for (Tuple<AbstractAdditionalTokensInfo, IPythonNature> tuple : additionalInfoAndNature) {
AdditionalInfoAndIInfo found = checkAdditionalInfo(resource,
resource.info.getName(), tuple.o1);
if (found != null) {
return found;
}
}
} catch (Exception e) {
Log.log(e);
}
}
}
}
// for(IPythonNature pythonNature:natures){
// //Try to find in one of the natures... if we don't find it, return null, as that means
// //it doesn't exist anymore!
// ICodeCompletionASTManager astManager = pythonNature.getAstManager();
// if(astManager == null){
// continue;
// }
// List<ItemPointer> pointers = new ArrayList<ItemPointer>();
// AnalysisPlugin.getDefinitionFromIInfo(pointers, astManager, pythonNature, resource.info, completionCache);
// if(pointers.size() > 0){
// return resource;
// }
// }
}
return null;
}
private AdditionalInfoAndIInfo checkAdditionalInfo(AdditionalInfoAndIInfo resource, String name,
AbstractAdditionalTokensInfo additionalInfoToSearch) {
Collection<IInfo> tokensEqualTo = additionalInfoToSearch.getTokensEqualTo(name,
AbstractAdditionalTokensInfo.TOP_LEVEL | AbstractAdditionalTokensInfo.INNER);
for (IInfo iInfo : tokensEqualTo) {
if (iInfo.equals(resource.info)) {
return resource;
}
}
return null;
}
protected void storeItemToMemento(Object item, IMemento element) {
AdditionalInfoAndIInfo resource = (AdditionalInfoAndIInfo) item;
InfoFactory infoFactory = new InfoFactory(resource);
infoFactory.saveState(element);
}
}
public ICallbackWithListeners getOnControlCreated() {
return onControlCreated;
}
public ICallbackWithListeners getOnControlDisposed() {
return onControlDisposed;
}
}